import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;

public class NeuronInputField extends DrawObject
{
    // DrawObject abilities:
    @Override
    public boolean IsNeuronInputField()
    {
        return true;
    }

    @Override
    public boolean CanBeDeletedByUser()
    {
        return false;
    };

    @Override
    public boolean CanBeExcitedByUser()
    {
        return true;
    };

    @Override
    public boolean CanBeMovedByUser()
    {
        return false;
    }

    @Override
    public boolean CanBeSelectedByUser()
    {
        return false;
    }
    // end of DrawObject abilities

    public NeuronInputField(Point Pos)
    {
        SetPosNoAlignOnGrid(Pos);
        SetSize(new Point(80, 20)); // we need 10 x-pixels for excitement bar plus 10 pixels gap (looks better)
        InitializeExcitement(ExcitementOnceMilliseconds);
    };

    @Override
    protected void AddExcitementForNextTick(double ExcitementAdd)
    {
        // Neuron: NeuronInputNodes DIRECTLY call this.ExciteAsPartner() instead!

        // reset
        ExcitementCurrent = 0.0;
        ExcitementAddForNextTick = 0.0;
    };

    @Override
    protected void AfterCollectingAllExcitementForNextTick()
    {
        // reset
        ExcitementCurrent = 0.0;
        ExcitementAddForNextTick = 0.0;
    };

    @Override
    public void Draw(Graphics g)
    {
        if (!(IsWithinWindowBounds(this, 0)))
            return;

        // background (overdraw grid)
        if (IsHighlighted || IsPartOfMultiSelection)
            SetColor(g, Color.GREEN);
        else
            SetColor(g, Color.LIGHT_GRAY);
        FillRect(g, Pos.x, Pos.y, Size.x, Size.y);

        // strength bar
        if (IsNegative)
            SetColor(g, Color.BLUE);
        else
            SetColor(g, Color.MAGENTA);
        FillRect(g, Pos.x, Pos.y, Strength * Size.x / StrengthMax, Size.y);

        // ExcitementTriggerValue marking
        /*
        SetColor(g, Color.RED);
        g.drawRect(Pos.x + (int) (ExcitementTriggerValue * Size.x), Pos.y, 0, Size.y);
        */

        // Frame
        if (IsHighlighted || IsPartOfMultiSelection)
            SetColor(g, Color.BLACK);
        else
            SetColor(g, GetExcitementColor());
        DrawRect(g, Pos.x, Pos.y, Size.x, Size.y);

        // level markings
        /*
        SetColor(g, Color.BLACK);
        int Stepx = Size.x / StrengthMax;
        for (int m = 0; m < StrengthMax; m ++)
        {
        	g.drawLine(Pos.x + m * Stepx, Pos.y + 1, Pos.x + m * Stepx, Pos.y + 1 + 4);
        }
        */
    }

    protected void ExciteAsPartner(double ExcitementCurrent, DrawObject Sender)
    {
        // NOTE: we need to forward excitement instantly to avoid
        // annoying delays, but Neuron must first "collect" all
        // excitement values (negative ones might come along, too)
        // before "deciding" if to activate NeuronOutputNode!

        double Sign = +1.0;

        if (IsNegative && !(((Neuron) this.ExcitementPartner).IsInLearningNeuronState))
            Sign = -1.0;

        double FieldWeight = Sign * (double) Strength / (double) StrengthMax;

        double ForwardedWeightedExcitement = LimitExcitementAllowNegative(ExcitementCurrent * FieldWeight);

        if (DoesActivate(ExcitementCurrent)) // do NOT use WEIGHTED Excitement value, because it is only important if InputNODE was/is activated!
        {
            ExcitementPartnerServedInThisTick = true;
        }

        ((Neuron) this.ExcitementPartner).ExciteAsPartner(ForwardedWeightedExcitement, this);

        ShiftExcitementHistory(ExcitementCurrent);
    }

    @Override
    protected void LoseExcitementAndAddExcitementForNextTick()
    {
        // uses the ExciteAsPartner() excitement donation scheme!

        ExcitementCurrent = 0.0;
        ExcitementAddForNextTick = 0.0;
        ExcitementPartnerServedInThisTick = false;
    }

    @Override
    public void Move(Point MoveAmount)
    {
        SetPosNoAlignOnGrid(new Point(Pos.x + MoveAmount.x, Pos.y + MoveAmount.y));
    }

    @Override
    public void ResetState()
    { // meant to be called for EVERY DrawObject before beginning a new simulation

        if (((Neuron) this.ExcitementPartner).IsInLearningNeuronState && IsNegative)
            Strength = StrengthMax;
        else if (((Neuron) this.ExcitementPartner).IsInLearningNeuronState)
            Strength = 0;

        ExcitementCurrent = 0.0;
        ExcitementAddForNextTick = 0.0;

        ExcitementPartnerServedInThisTick = false;

        for (int m = 0; m < ExcitementHistoryLength; m++)
            ExcitementHistory[m] = 0.0;
        ExcitementHistoryWriteIndex = 0;
    }

    public void Toggle()
    {
        if (IsNegative && (((Neuron) this.ExcitementPartner).IsInLearningNeuronState))
        {
            Strength = StrengthMax;
        }
        else if (!(((Neuron) this.ExcitementPartner).IsInLearningNeuronState))
        {
            Strength += StrengthIncreaseStep;
            if (Strength > StrengthMax)
                Strength = 0;
        }
    }

    @Override
    public void ToggleIsInLearningNeuronState()
    {
        ParentDrawObject.ToggleIsInLearningNeuronState();
    }

    public void ToggleIsNegative()
    {
        IsNegative = !IsNegative;

        if ((((Neuron) this.ExcitementPartner).IsInLearningNeuronState) && IsNegative)
        {
            SetStrength(StrengthMax); // means internal weight 1.0, to avoid confusion of user
            SetStrengthTrigger(StrengthMax); // not used anyway
        }
        if ((((Neuron) this.ExcitementPartner).IsInLearningNeuronState) && !IsNegative)
        {
            SetStrength(0); // "nothing learned yet"
            SetStrengthTrigger(StrengthMax); // not used anyway
        }
    }
}
